1use std::ffi::CString;
8
9#[allow(unused)]
10use winapi::um::synchapi::{CreateMutexA, ReleaseMutex, WaitForSingleObject};
11use winapi::um::winnt::HANDLE;
12use std::io::Write;
13use std::net::TcpStream;
14pub type MutexHandle = HANDLE;
15pub enum ResultCode {
16 Ok,
17 Abandoned = winapi::um::winbase::WAIT_ABANDONED as isize,
18}
19
20use std::process::{Command, Stdio};
21use std::thread::sleep;
22
23pub fn powershell_message_box_non_blocking(message: &str) {
24 let title = "UNPISPAS SERVICIO IMPRESION";
25 let command = format!(
26 "[reflection.assembly]::LoadWithPartialName('System.Windows.Forms')|out-null;[windows.forms.messagebox]::Show('{}', '{}')",
27 message, title
28 );
29
30 let result = Command::new("powershell")
31 .arg("-WindowStyle")
32 .arg("Hidden")
33 .arg("-Command")
34 .arg(command)
35 .stdout(Stdio::null())
36 .stderr(Stdio::piped())
37 .spawn();
38
39 if let Err(e) = result {
40 tracing::error!("Error al ejecutar el comando PowerShell: {}", e);
41 }
42}
43
44pub fn create_mutex(name: &str) -> MutexHandle {
45 unsafe {
46 let mutex_name = CString::new(name).unwrap();
47 CreateMutexA(std::ptr::null_mut(), 1, mutex_name.as_ptr())
48 }
49}
50
51pub unsafe fn close_handle(handle: MutexHandle) {
52 unsafe {
53 winapi::um::handleapi::CloseHandle(handle);
54 }
55}
56
57pub fn send_messagebox_to_tray(message: String) -> crate::PisPasResult<()> {
58 match TcpStream::connect(crate::CHANNEL_NAME) {
59 Ok(mut stream) => {
60 let action = crate::service::Action::MessageBox(message);
62 match stream.write_all(action.to_string().as_bytes()) {
63 Ok(_) => {
64 tracing::info!("Message sent: {}", action.to_string());
65 return Ok(());
66 }
67 Err(e) => {
68 tracing::error!("Failed to write to socket in dedicated thread {}", e);
69 return Err(anyhow::anyhow!("Failed to write to socket in dedicated thread {}", e));
70 }
71 }
72 }
73 Err(e) => {
74 tracing::error!("Error connecting to pispas-channel: {}", e);
75 }
76 }
77 Ok(())
78}
79
80pub fn stop_pispas_modules() -> crate::PisPasResult<()> {
81 match TcpStream::connect(crate::CHANNEL_NAME) {
82 Ok(mut stream) => {
83 match stream.write_all(crate::service::Action::Stop.to_string().as_bytes()) {
84 Ok(_) => {
85 tracing::info!("Message sent: {}", crate::service::Action::Stop.to_string());
86 return Ok(());
87 }
88 Err(e) => {
89 tracing::error!("Failed to write to socket in dedicated thread {}", e);
90 return Err(anyhow::anyhow!("Failed to write to socket in dedicated thread {}", e));
91 }
92 }
93 }
94 Err(e) => {
95 tracing::error!("Error connecting to pispas-channel: {}", e);
96 }
97 }
98 sleep(std::time::Duration::from_secs(1));
99 Ok(())
100}
101
102pub unsafe fn wait_for_single_object(handle: MutexHandle, timeout: u32) -> ResultCode {
103 let code = unsafe { WaitForSingleObject(handle, timeout) };
104 if !(code == winapi::um::winbase::WAIT_ABANDONED || code == winapi::um::winbase::WAIT_OBJECT_0) {
105 ResultCode::Abandoned
106 } else {
107 ResultCode::Ok
108 }
109}
110
111pub unsafe fn release_mutex(handle: MutexHandle) {
112 unsafe { ReleaseMutex(handle) };
113}
114
115#[allow(non_camel_case_types, non_snake_case)]
116#[cfg(target_os = "windows")]
117pub mod native {
118 use std::{
119 ffi::CString,
120 ptr::null_mut,
121 thread::sleep,
122 time::Duration,
123 };
124 use widestring::WideCString;
125 use winapi::{
126 shared::minwindef::{BOOL, DWORD},
127 um::{
128 handleapi::CloseHandle,
129 processthreadsapi::{CreateProcessAsUserW, OpenProcess},
130 winbase::{CREATE_UNICODE_ENVIRONMENT},
131 winnt::{HANDLE, PROCESS_ALL_ACCESS, PROCESS_SUSPEND_RESUME},
132 },
133 };
134 use windows_sys::Win32::System::Environment::{
135 CreateEnvironmentBlock, DestroyEnvironmentBlock,
136 };
137 use windows_sys::Win32::Foundation::GetLastError;
138 use std::ptr;
139 use std::ffi::OsStr;
140
141 const CREATE_NO_WINDOW: u32 = 0x0800_0000;
143
144 const POWERSHELL_TIMEOUT: Duration = Duration::from_secs(30);
149
150 fn run_powershell_with_timeout(
162 args: &[&str],
163 timeout: Duration,
164 ) -> Result<std::process::Output, String> {
165 use std::io::Read;
166 use std::os::windows::process::CommandExt;
167
168 let mut full_args: Vec<String> = vec![
169 "-NoProfile".into(),
170 "-NonInteractive".into(),
171 "-WindowStyle".into(),
172 "Hidden".into(),
173 "-ExecutionPolicy".into(),
174 "Bypass".into(),
175 ];
176 full_args.extend(args.iter().map(|a| (*a).to_string()));
177
178 let mut child = std::process::Command::new("powershell")
179 .args(&full_args)
180 .stdin(std::process::Stdio::null())
181 .stdout(std::process::Stdio::piped())
182 .stderr(std::process::Stdio::piped())
183 .creation_flags(CREATE_NO_WINDOW)
184 .spawn()
185 .map_err(|e| format!("spawn powershell: {}", e))?;
186
187 let stdout_handle = child.stdout.take().map(|mut s| {
194 std::thread::spawn(move || {
195 let mut buf = Vec::new();
196 let _ = s.read_to_end(&mut buf);
197 buf
198 })
199 });
200 let stderr_handle = child.stderr.take().map(|mut s| {
201 std::thread::spawn(move || {
202 let mut buf = Vec::new();
203 let _ = s.read_to_end(&mut buf);
204 buf
205 })
206 });
207
208 let collect_output = |stdout_h: Option<std::thread::JoinHandle<Vec<u8>>>,
214 stderr_h: Option<std::thread::JoinHandle<Vec<u8>>>|
215 -> (Vec<u8>, Vec<u8>) {
216 let so = stdout_h.and_then(|h| h.join().ok()).unwrap_or_default();
217 let se = stderr_h.and_then(|h| h.join().ok()).unwrap_or_default();
218 (so, se)
219 };
220
221 let start = std::time::Instant::now();
222 let status = loop {
223 match child.try_wait() {
224 Ok(Some(s)) => break s,
225 Ok(None) => {
226 if start.elapsed() > timeout {
227 let _ = child.kill();
234 let _ = child.wait();
235 let _ = collect_output(stdout_handle, stderr_handle);
236 return Err(format!("powershell exceeded {:?}, killed", timeout));
237 }
238 std::thread::sleep(Duration::from_millis(100));
239 }
240 Err(e) => {
241 let _ = child.kill();
246 let _ = child.wait();
247 let _ = collect_output(stdout_handle, stderr_handle);
248 return Err(format!("wait powershell: {}", e));
249 }
250 }
251 };
252
253 let (stdout, stderr) = collect_output(stdout_handle, stderr_handle);
256
257 Ok(std::process::Output { status, stdout, stderr })
258 }
259 use std::os::windows::ffi::OsStrExt;
260 use winapi::um::winbase::WTSGetActiveConsoleSessionId;
261 use std::ffi::c_void; const WTS_CURRENT_SERVER_HANDLE: HANDLE = null_mut();
264 const WTS_USER_NAME: DWORD = 5;
265
266 #[repr(C)]
267 struct WTS_SESSION_INFO {
268 SessionId: DWORD,
269 pWinStationName: *mut u16,
270 State: DWORD,
271 }
272
273 enum WTSSessionState {
274 WTSActive = 0,
275 WTSConnected = 1,
276 WTSDisconnected = 4,
277 }
278
279 #[link(name = "wtsapi32")]
280 extern "system" {
281 fn LoadLibraryA(lpFileName: *const i8) -> HANDLE;
282 fn GetProcAddress(hModule: HANDLE, lpProcName: *const i8) -> *mut c_void;
283 fn FreeLibrary(hLibModule: HANDLE) -> BOOL;
284 fn WTSQueryUserToken(SessionId: DWORD, phToken: *mut HANDLE) -> BOOL;
285 fn WTSQuerySessionInformationW(hServer: HANDLE, SessionId: DWORD, WTSInfoClass: DWORD, ppBuffer: *mut *mut u16, pBytesReturned: *mut DWORD) -> BOOL;
287 fn WTSFreeMemory(pMemory: *mut c_void);
288 }
289
290 fn load_wtsapi32_module() -> Result<HANDLE, &'static str> {
291 let library_name = CString::new("wtsapi32.dll").expect("CString::new failed");
292 let h_module = unsafe { LoadLibraryA(library_name.as_ptr()) };
293 if h_module.is_null() {
294 Err("Failed to load wtsapi32.dll")
295 } else {
296 Ok(h_module)
297 }
298 }
299
300 fn get_function_addresses(h_module: HANDLE) -> Result<
301 (
302 extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL,
303 extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL
304 ),
305 &'static str
306 > {
307 let function_name = CString::new("WTSEnumerateSessionsW").unwrap();
308 let enum_sessions: Option<extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL> = unsafe {
309 std::mem::transmute(GetProcAddress(h_module, function_name.as_ptr()))
310 };
311
312 let function_name_cstr = CString::new("WTSQuerySessionInformationW").unwrap();
313 let query_session: Option<extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL> = unsafe {
314 std::mem::transmute(GetProcAddress(h_module, function_name_cstr.as_ptr()))
315 };
316
317 match (enum_sessions, query_session) {
318 (Some(enumerate_sessions_func), Some(query_session_func)) => Ok((enumerate_sessions_func, query_session_func)),
319 _ => Err("Failed to get procedure addresses.")
320 }
321 }
322
323 fn to_wide_cstring(value: &str) -> WideCString {
324 WideCString::from_str(value).unwrap()
325 }
326
327 fn create_command_line_wide(executable_path: &str, script_path: &str) -> WideCString {
328 let cmd = format!("\"{}\" \"{}\"", executable_path.trim_end_matches('\\'), script_path);
329 WideCString::from_str(&cmd).unwrap()
330 }
331
332 fn wait_for_user_session(session_id: DWORD) -> anyhow::Result<()> {
333 loop {
334 let state = query_session_state(session_id)?;
335 if state == WTSSessionState::WTSActive as DWORD {
336 return Ok(()); }
338 sleep(Duration::from_millis(500));
339 }
340 }
341
342 fn query_session_state(session_id: DWORD) -> anyhow::Result<DWORD> {
343 let mut p_buffer: *mut u16 = null_mut();
344 let mut bytes_returned: DWORD = 0;
345
346 unsafe {
347 let success = WTSQuerySessionInformationW(
348 WTS_CURRENT_SERVER_HANDLE,
349 session_id,
350 0, &mut p_buffer,
352 &mut bytes_returned,
353 );
354
355 if success == 0 {
356 return Err(anyhow::anyhow!("Failed to query session information"));
357 }
358
359 let state = *p_buffer as DWORD;
360 WTSFreeMemory(p_buffer as *mut c_void);
361 Ok(state)
362 }
363 }
364
365 fn wait_for_any_active_session(enumerate_sessions_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL) -> anyhow::Result<()> {
366 loop {
367 let mut session_info_ptr: *mut WTS_SESSION_INFO = null_mut();
368 let mut session_count: DWORD = 0;
369
370 let success = enumerate_sessions_func(
371 WTS_CURRENT_SERVER_HANDLE,
372 0,
373 1,
374 &mut session_info_ptr,
375 &mut session_count,
376 );
377
378 if success != 0 {
379 unsafe {
380 for i in 0..session_count {
381 let session = session_info_ptr.offset(i as isize);
382
383 if (*session).State == WTSSessionState::WTSActive as u32 {
384 return Ok(()); }
386 }
387 }
388 }
389 sleep(Duration::from_secs(1));
390 }
391 }
392
393 fn launch_process_for_user(executable_path: &str, command_line: &str, session_id: DWORD) -> Option<HANDLE> {
394 unsafe {
395 if let Err(e) = wait_for_user_session(session_id) {
396 tracing::error!("Failed to wait for user session: {}", e);
397 return None;
398 }
399
400 sleep(Duration::from_secs(1));
402
403 let mut user_token: HANDLE = null_mut();
404 if WTSQueryUserToken(session_id, &mut user_token) != 0 {
405 let exe_path_wide = to_wide_cstring(executable_path);
406 let cmd_line_wide = create_command_line_wide(executable_path, command_line);
407 let path_current_dir_wide = to_wide_cstring(&crate::paths::bin_dir().display().to_string());
408
409 tracing::info!("Executable path: {}", executable_path);
410 tracing::info!("Command line: {:?}", cmd_line_wide);
411
412 let mut startup_info = std::mem::zeroed::<winapi::um::processthreadsapi::STARTUPINFOW>();
413 startup_info.cb = std::mem::size_of::<winapi::um::processthreadsapi::STARTUPINFOW>() as u32;
414
415 let desktop_name_wide: Box<Vec<u16>> = Box::new(to_wide_string("WinSta0\\Default"));
416 startup_info.lpDesktop = desktop_name_wide.as_ptr() as *mut u16;
417
418 let mut environment_block: *mut c_void = std::ptr::null_mut();
419 if CreateEnvironmentBlock(&mut environment_block, user_token as *mut c_void, 1) == 0 {
421 let error_code = GetLastError();
422 tracing::error!("Failed to create environment block for user. Error code: {}", error_code);
423 CloseHandle(user_token);
424 return None;
425 }
426
427 let _keep_alive = desktop_name_wide; let mut process_info = std::mem::zeroed::<winapi::um::processthreadsapi::PROCESS_INFORMATION>();
430 tracing::info!("exe_path: {:?}, command_line: {:?}, path_current_dir: {:?}", cmd_line_wide, cmd_line_wide, path_current_dir_wide);
431
432
433 let success = CreateProcessAsUserW(
434 user_token,
435 exe_path_wide.as_ptr() as *mut u16,
436 cmd_line_wide.as_ptr() as *mut u16,
437 null_mut(),
438 null_mut(),
439 0,
440 CREATE_UNICODE_ENVIRONMENT,
441 environment_block as *mut winapi::ctypes::c_void,
442 path_current_dir_wide.as_ptr() as *mut u16,
443 &mut startup_info,
444 &mut process_info,
445 );
446
447 if success != 0 {
448 CloseHandle(user_token);
449 DestroyEnvironmentBlock(environment_block);
450 return Some(process_info.hProcess);
451 } else {
452 let error_code = GetLastError();
453 tracing::error!("CreateProcessAsUserW failed: Error code = {}", error_code);
454 }
455 DestroyEnvironmentBlock(environment_block);
456 CloseHandle(user_token);
457 } else {
458 let error_code = GetLastError();
459 tracing::error!("WTSQueryUserToken failed for session {}: Error code = {}", session_id, error_code);
460 }
461 }
462
463 None
464 }
465
466 fn enumerate_and_launch(
467 executable_path: &str,
468 command_line: &str,
469 all_users: bool,
470 first_execution: bool,
471 enumerate_sessions_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut WTS_SESSION_INFO, *mut DWORD) -> BOOL,
472 query_session_func: extern "system" fn(HANDLE, DWORD, DWORD, *mut *mut c_void, *mut DWORD) -> BOOL
473 ) -> crate::PisPasResult<Option<HANDLE>> {
474 wait_for_any_active_session(enumerate_sessions_func)?;
475
476 let mut session_info_ptr: *mut WTS_SESSION_INFO = null_mut();
477 let mut session_count: DWORD = 0;
478
479 let success = enumerate_sessions_func(
480 WTS_CURRENT_SERVER_HANDLE,
481 0,
482 1,
483 &mut session_info_ptr,
484 &mut session_count,
485 );
486
487 if success != 0 {
488 tracing::info!("Found {} sessions.", session_count);
489 unsafe {
490 for i in 0..session_count {
491 if session_info_ptr.is_null() {
492 return Err(anyhow::anyhow!("Session info pointer is null"));
493 }
494 let session = session_info_ptr.offset(i as isize);
495
496 if (*session).State == WTSSessionState::WTSActive as u32 ||
497 (*session).State == WTSSessionState::WTSConnected as u32 ||
498 (*session).State == WTSSessionState::WTSDisconnected as u32 {
499 let mut data: *mut c_void = null_mut();
500 let mut bytes: DWORD = 0;
501
502 if query_session_func(WTS_CURRENT_SERVER_HANDLE, (*session).SessionId, WTS_USER_NAME, &mut data, &mut bytes) != 0 {
503 let user_name: *const u16 = data as *const u16;
504 let user_slice = std::slice::from_raw_parts(user_name, bytes as usize / 2);
505 match String::from_utf16(user_slice) {
506 Ok(mut user) => {
507 user = user.trim_end_matches('\0').to_string(); if !user.trim().is_empty() && (*session).SessionId != 0 {
509 tracing::info!("Session {}: User = {}", (*session).SessionId, user);
510 let bin_dir = crate::paths::bin_dir().display().to_string();
511 tracing::info!("bin_dir: {}", bin_dir);
512 if first_execution {
513 if let Err(e) = set_permissions_robust(&bin_dir, &user) {
514 tracing::error!("Failed to set permissions for user {}: {}", user, e);
515 } else {
516 tracing::info!("Permissions set for user {}", user);
517 sleep(Duration::from_secs(1));
518 }
519 }
520
521 if let Some(process_handle) = launch_process_for_user(executable_path, command_line, (*session).SessionId) {
522 tracing::info!("Process launched for user {}", user);
523 if !all_users {
524 return Ok(Some(process_handle));
525 }
526 } else {
527 tracing::error!("Failed to launch process for user {}", user);
528 }
529 } else {
530 tracing::warn!("Ignoring session {}: User name is empty or session ID is 0", (*session).SessionId);
531 }
532 }
533 Err(e) => {
534 tracing::info!("Failed to convert UTF-16 to string: {}", e);
535 return Err(anyhow::anyhow!("Error converting UTF-16 to string"));
536 }
537 }
538 }
539 }
540 }
541 }
542 } else {
543 return Err(anyhow::anyhow!("Error enumerate_sessions_func"));
544 }
545 Ok(None)
546 }
547 fn to_wide_string(s: &str) -> Vec<u16> {
549 OsStr::new(s).encode_wide().chain(Some(0)).collect()
550 }
551 struct WideString {
552 inner: Vec<u16>,
553 }
554
555 impl WideString {
556 pub fn new(s: &str) -> Self {
557 Self {
558 inner: to_wide_string(s),
559 }
560 }
561
562 pub fn as_ptr(&self) -> *const u16 {
563 self.inner.as_ptr()
564 }
565 }
566 pub fn launch_process_as_logged_in_user(executable_path: &str, command_line: &str, first_launch: bool) -> crate::PisPasResult<Option<HANDLE>> {
568 const TOTAL_LOGGED_USER_BUDGET: Duration = Duration::from_secs(60);
575 let loop_started = std::time::Instant::now();
576
577 let mut attempts = 0;
578 let user = loop {
579 if attempts >= 20 {
580 return Err(anyhow::anyhow!("No user found logged in after multiple attempts."));
581 }
582 let budget_remaining = TOTAL_LOGGED_USER_BUDGET
588 .checked_sub(loop_started.elapsed())
589 .unwrap_or_default();
590 if budget_remaining.is_zero() {
591 return Err(anyhow::anyhow!(
592 "No user found logged in within {:?} budget — giving up to avoid update hang.",
593 TOTAL_LOGGED_USER_BUDGET
594 ));
595 }
596 let attempt_timeout = std::cmp::min(POWERSHELL_TIMEOUT, budget_remaining);
597
598 let result = run_powershell_with_timeout(
602 &["-Command", "(Get-WmiObject -Class Win32_ComputerSystem).UserName"],
603 attempt_timeout,
604 );
605
606 match result {
607 Ok(output) if output.status.success() => {
608 let user = String::from_utf8_lossy(&output.stdout).trim().to_string();
609 if !user.is_empty() {
610 break user;
611 } else {
612 tracing::error!("No user found logged in, retrying...");
613 }
614 }
615 Ok(output) => {
616 let stderr = String::from_utf8_lossy(&output.stderr);
617 tracing::error!("Error getting logged in user: {}, retrying...", stderr);
618 }
619 Err(e) => {
620 return Err(anyhow::anyhow!(
627 "powershell get-user failed, aborting: {}",
628 e
629 ));
630 }
631 }
632
633 attempts += 1;
634 sleep(Duration::from_secs(1));
635 };
636
637
638 tracing::info!("Logged in user: {}", user);
639 let username = user.split('\\').last().unwrap_or(&user);
640 tracing::info!("Extracted username: {}", username);
641
642 if first_launch{
644 if let Err(e) = set_permissions_robust(executable_path, &username) {
645 tracing::error!("Failed to set permissions for user {}: {}", user, e);
646 } else {
647 tracing::info!("Permissions set for user {}", user);
648 sleep(Duration::from_secs(1));
649 }
650 }
651
652 let session_id = unsafe { WTSGetActiveConsoleSessionId() };
654 if session_id == u32::MAX {
655 let error_code = unsafe { GetLastError() };
656 tracing::error!("Failed to get active session ID: {}", error_code);
657 return Err(anyhow::anyhow!("Failed to get active session ID: {}", error_code));
658 }
659
660 let mut user_token: HANDLE = ptr::null_mut();
661 if unsafe { WTSQueryUserToken(session_id, &mut user_token) } == 0 {
662 let error_code = unsafe { GetLastError() };
663 tracing::error!("Failed to get user token: {}", error_code);
664 return Err(anyhow::anyhow!("Failed to get user token: {}", error_code));
665 }
666
667 let full_command = format!("\"{}\" \"{}\"", executable_path, command_line);
668 let command_utf16 = OsStr::new(&full_command).encode_wide().chain(Some(0)).collect::<Vec<u16>>();
669
670 if first_launch {
671 sleep(Duration::from_secs(4));
672 }
673 let mut startup_info = unsafe { std::mem::zeroed::<winapi::um::processthreadsapi::STARTUPINFOW>() };
674 startup_info.cb = std::mem::size_of::<winapi::um::processthreadsapi::STARTUPINFOW>() as u32;
675 let desktop_name = WideString::new("WinSta0\\Default");
676 startup_info.lpDesktop = desktop_name.as_ptr() as *mut u16;
677
678 let mut process_info = unsafe { std::mem::zeroed::<winapi::um::processthreadsapi::PROCESS_INFORMATION>() };
680
681 let success = unsafe {
683 CreateProcessAsUserW(
684 user_token,
685 ptr::null(),
686 command_utf16.as_ptr() as *mut _,
687 ptr::null_mut(),
688 ptr::null_mut(),
689 0,
690 CREATE_UNICODE_ENVIRONMENT,
691 ptr::null_mut(),
692 ptr::null(),
693 &mut startup_info,
694 &mut process_info,
695 )
696 };
697
698 unsafe { CloseHandle(user_token) };
699
700 if success == 0 {
701 let error_code = unsafe { GetLastError() };
702 tracing::error!("Failed to launch process: {}", error_code);
703 Err(anyhow::anyhow!("Failed to launch process: {}", error_code))
704 } else {
705 tracing::info!("Process launched successfully in the context of the logged-in user.");
706 Ok(Some(process_info.hProcess))
707 }
708 }
709
710 pub fn run_in_all_sessions(executable_path: &str, command_line: &str, all_user: bool, first_execution: bool) -> crate::PisPasResult<Option<HANDLE>> {
711 match load_wtsapi32_module() {
712 Ok(h_module) => {
713 match get_function_addresses(h_module) {
714 Ok((enumerate_sessions_func, query_session_func)) => {
715 tracing::info!("Enumerating sessions...");
716 let result = enumerate_and_launch(executable_path, command_line, all_user, first_execution, enumerate_sessions_func, query_session_func);
717 unsafe { FreeLibrary(h_module); }
718 result
719 }
720 Err(e) => {
721 unsafe { FreeLibrary(h_module); }
722 tracing::error!("{}", e);
723 Err(anyhow::anyhow!("Error getting function addresses"))
724 }
725 }
726 }
727 Err(e) => {
728 tracing::error!("error {}", e);
729 Err(anyhow::anyhow!("Error loading wtsapi32 module"))
730 }
731 }
732 }
733
734 pub fn open_process(pid: u32) -> Option<HANDLE> {
735 let desired_access = PROCESS_ALL_ACCESS | PROCESS_SUSPEND_RESUME;
736 let process_handle = unsafe { OpenProcess(desired_access, 0, pid) };
737
738 if process_handle.is_null() {
739 unsafe {
740 tracing::info!("Error obtaining process handle. Error: {}", GetLastError());
741 }
742 None
743 } else {
744 tracing::info!("Process handle: {:?}", process_handle);
745 Some(process_handle)
746 }
747 }
748
749 pub fn set_permissions_robust(path: &str, user: &str) -> Result<(), String> {
751 let user = user.trim_end_matches('\0');
752 let path = path.trim_end_matches('\0');
753
754 let powershell_script = format!(
755 r#"
756 try {{
757 $path = '{}'
758 $user = '{}'
759 $successCount = 0
760 $errorCount = 0
761
762 # Set permissions on root folder first
763 try {{
764 $acl = Get-Acl $path
765 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','ContainerInherit,ObjectInherit','None','Allow')
766 $acl.SetAccessRule($rule)
767 Set-Acl $path $acl
768 $successCount++
769 Write-Output "Root permissions set: $path"
770 }} catch {{
771 $errorCount++
772 Write-Warning "Failed to set root permissions: $($_.Exception.Message)"
773 }}
774
775 # Process all child items
776 Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object {{
777 try {{
778 $acl = Get-Acl $_.FullName
779 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','ContainerInherit,ObjectInherit','None','Allow')
780 $acl.SetAccessRule($rule)
781 Set-Acl $_.FullName $acl
782 $successCount++
783 }} catch {{
784 $errorCount++
785 Write-Warning "Failed: $($_.FullName) - $($_.Exception.Message)"
786 }}
787 }}
788
789 Write-Output "Completed: $successCount successful, $errorCount errors"
790 }} catch {{
791 Write-Error $_.Exception.Message
792 exit 1
793 }}
794 "#,
795 path, user
796 );
797
798 tracing::info!("Executing robust recursive permissions for: {}", path);
799
800 let output = match run_powershell_with_timeout(
804 &["-Command", &powershell_script],
805 Duration::from_secs(60),
806 ) {
807 Ok(o) => o,
808 Err(e) => return Err(format!("set_permissions_robust powershell failed: {}", e)),
809 };
810
811 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
812 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
813
814 tracing::info!("robust_permissions stdout: {}", stdout);
815 if !stderr.is_empty() {
816 tracing::warn!("robust_permissions stderr: {}", stderr);
817 }
818
819 if !output.status.success() {
820 return Err(format!("Failed to set robust permissions: {}", stderr));
821 }
822
823 Ok(())
824 }
825 pub fn set_permissions_for_user(path: &str, user: &str) -> Result<(), String> {
826 let user = user.trim_end_matches('\0');
827 let path = path.trim_end_matches('\0');
828 let powershell_script = format!(
834 r#"
835 try {{
836 $path = '{}'
837 $user = '{}'
838 $acl = Get-Acl $path
839 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user,'FullControl','None','None','Allow')
840 $acl.SetAccessRule($rule)
841 Set-Acl $path $acl
842 Write-Output "Permissions set successfully"
843 }} catch {{
844 Write-Error $_.Exception.Message
845 exit 1
846 }}
847 "#,
848 path, user
849 );
850
851 tracing::info!("powershell_script: {:?}", powershell_script);
852
853 let output = match run_powershell_with_timeout(
854 &["-Command", &powershell_script],
855 POWERSHELL_TIMEOUT,
856 ) {
857 Ok(o) => o,
858 Err(e) => return Err(format!("set_permissions_for_user powershell failed: {}", e)),
859 };
860
861 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
862 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
863
864 tracing::info!("set_permissions_for_user stdout: {}", stdout);
865 tracing::info!("set_permissions_for_user stderr: {}", stderr);
866
867 if !output.status.success() {
868 return Err(format!("Failed to set permissions: {}", stderr));
869 }
870
871 Ok(())
872 }
873}
874
875pub fn get_name_service() -> String {
876 let path = match std::env::current_exe() {
877 Ok(path) => path,
878 Err(e) => {
879 println!("Error al obtener el nombre del archivo ejecutable: {:?}", e);
880 return "ServiceDefault".to_string();
881 }
882 };
883 let name = match path.file_name() {
884 Some(name_without_extension) => name_without_extension.to_str().unwrap(),
885 None => {
886 println!("Error al obtener el nombre del archivo ejecutable");
887 return "ServiceDefault".to_string();
888 }
889 };
890 let _name_without_extension = name.replace(".exe", "");
891 _name_without_extension.to_string()
892}